#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "HttpCmdProcess.h"
#include "StreamFileServer.h"
#include "Thread/ThreadPool.h"
#include "Util/onceToken.h"
#include "Protocol/ProtocolApi.h"

using namespace Protocol;

StreamFileServer *StreamFileServer::sm_streamfileServer = nullptr;
mutex StreamFileServer::sm_mutex;
const string CompressFileName = "/mnt/sd/streamer/image.zip";



void StreamFileServerInit(){
	StreamFileServer::instance();
}

StreamFileServer::StreamFileServer(){
	RegistRPC();
}

void StreamFileServer::RegistRPC(){
	RPCMethodMap method_map;
	method_map.emplace("SetFileInfo", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return SetFileInfo(request, inMap, response, outMap);
	}));
	
	method_map.emplace("UploadStream", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return TransferStream(request, inMap, response, outMap);
	}));

	method_map.emplace("GetFileInfo", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return GetFileInfo(request, inMap, response, outMap);
	}));

	method_map.emplace("DownloadStream", CRPCFuntion::CreateRpc([this](const Json::Value &request, const BufferMap::Ptr &inMap, Json::Value &response, BufferMap::Ptr &outMap){
		return DownloadStream(request, inMap, response, outMap);
	}));


	IProtocolInterface::GetInstance()->RegistProtocol("StreamFile", method_map);
}


bool StreamFileServer::SetFileInfo(const Json::Value &request, const Fdt::BufferMap::Ptr &inMap, Json::Value &response, Fdt::BufferMap::Ptr &outMap){
	tracef("json------------------------>%s", request.toStyledString().c_str());
	if(!request.isMember("Param") || request["Param"].empty() ){
		return false;
	}
	const Json::Value &param = request["Param"];

	std::string name;
	if(!Json::getJsonString(param, "Filename", name)){
		tracef("name error");
		return false;
	}
	_file_name = name;

	std::string type;
	if(!Json::getJsonString(param, "Type", type)){
		tracef("type error");
		return false;
	}
	_type = type;

	std::string md5;
	if(!Json::getJsonString(param, "Md5", md5)){
		tracef("md5 error");
		return false;
	}
	_md5 = md5;

	int size;
	if(!Json::getJsonInt(param, "Size", size)){
		tracef("Size error");
		return false;
	}
	_total_size = size;
	
	//如果已经传输过相同的文件了就删除重新传输
	return Media::HttpCmdProcess::instance()->fileStreamReset(_file_name, _type);

}


bool StreamFileServer::TransferStream(const Json::Value &request, const Fdt::BufferMap::Ptr &inMap, Json::Value &response, Fdt::BufferMap::Ptr &outMap){
	//tracef("******************** entry  TransferStream *****************************\n%s",request.toStyledString().c_str());

	//response初始化
	API::ApiErr err = API::Success;
	std::string err_str;
    onceToken token(nullptr, [&response, &err, &err_str]() {
    	//tracef("OnceToken!!!!!!!!!!!!!!!!!!!!");
    	IProtocolInterface::GetInstance()->HttpSetError(response, err, err_str);
    });

	//参数校验
	if(!verifyStreamFile(request, err_str)){
		err =  API::InvalidArgs;
		tracef("verifyParam error %s", request.toStyledString().c_str());
		return false;
	}

	err_str = "StreamFile ";
	//获取传输文件流
	BufferPtr::Ptr bufferPtr =  inMap->get(_file_name);
	//inMap -> list();
	if(!bufferPtr){
		errorf("bufferPtr error");
		err_str = "Buffer error";
		err = API::InvalidArgs;
		return false;
	}
	if(_size != bufferPtr -> size()){
		errorf("bufferPtr size  error");
		err_str = "Buffer size  error";
		err = API::InvalidArgs;
		return false;
	}
	
	//tracef("bufferPtr->size %d", bufferPtr->size());

	if(!Media::HttpCmdProcess::instance()->streamWrite(_file_name,bufferPtr->data(), bufferPtr->size())){
		//errorf("file write error");
		err = API::OtherFailed;
		return false;
	}

	if( (unsigned int)_total_size == (unsigned int)_pos + _size){
		//tracef("entry verify the file complete");
		std::string md5 = strHex2Val(_md5.c_str(), _md5.size());
		if(!Media::HttpCmdProcess::instance()->fileVerify(_file_name,md5.c_str())){
			response["msg"] = "file transfer faild";
			return false;
		}
	}else if((unsigned int)_total_size < (unsigned int)_pos + _size){
		response["msg"] = "file transfer faild";
		return false;
	}

	return true;
}

bool StreamFileServer::verifyStreamFile(const Json::Value &request, std::string &str_err){
	if(!request.isMember("Param") || request["Param"].empty() ){
		return false;
	}
	const Json::Value &param = request["Param"];

	str_err = "Verify ";
	std::string name ;
	if(!Json::getJsonString(param, "Filename", name)){
		str_err += "name ";
		tracef("name error");
		return false;
	}
	if(name.empty() || name != _file_name){
		str_err += "name ";
		tracef("name error");
		return false;
	}
	
	int pos;
	if(!Json::getJsonInt(param, "Pos", pos)){
		str_err += "slice ";
		tracef("slice error");
		return false;
	}
	if(pos < 0){
		str_err += "slice ";
		tracef("slice error");
		return false;
	}
	_pos = pos;

	int size;
	if(!Json::getJsonInt(param, "Size", size)){
		str_err += "size ";
		tracef("size error");
		return false;
	}
	if(size <= 0){
		str_err += "size ";
		tracef("size error");
		return false;
	}
	_size = size;

	return true;
}


bool StreamFileServer::GetFileInfo(const Json::Value &request, const Fdt::BufferMap::Ptr &inMap, Json::Value &response, Fdt::BufferMap::Ptr &outMap){
	if(!request.isMember("Param")){
		errorf("parameter error\n");
		return false;
	}
	string filename;
	unsigned int size;
	string md5;
	if(!Media::HttpCmdProcess::instance()->GetFileInfo(request["Param"], filename, size, md5)){
		IProtocolInterface::GetInstance()->HttpSetError(response, API::FileNotExist);
		return false;
	}
	response["data"]["Param"]["Filename"] = filename;
	response["data"]["Param"]["Md5"] = md5;
	response["data"]["Param"]["Size"] = size;
	errorf("filename: %s     size: %u    md5:%s\n", filename.c_str(), size, md5.c_str());
	return true;
}


bool StreamFileServer::DownloadStream(const Json::Value &request, const Fdt::BufferMap::Ptr &inMap, Json::Value &response, Fdt::BufferMap::Ptr &outMap){
	//判断文件是否存在，不存在直接返回
	if (access(CompressFileName.c_str(), F_OK))
	{
		response["File"] = "";
		return true;
	}
	//存在给Response字段赋值
	response["File"] = CompressFileName;
	return true;
}









